import re

from django.db.models import Q
from django_redis import get_redis_connection
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from celery_tasks.email.tasks import send_verify_email
from goods.models import SKU
from users import constants
from users.models import User, Address


#
from users.utils import generate_find_password_accesstoken, decode_accesstoken

# 第三步-->验证短信验证码的序列化器类
class VerifySmscodeSerializer(serializers.Serializer):
    sms_code = serializers.CharField(label='短信验证码', write_only=True)
    user_id = serializers.IntegerField(label='用户编号', read_only=True)
    username = serializers.CharField(min_length=5, max_length=20, write_only=True)
    access_token = serializers.CharField(label='access_token', read_only=True)

    def validate(self, attrs):
        sms_code = attrs['sms_code']
        username = attrs['username']
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExit:
            raise serializers.ValidationError("用户名有误")

        sms_code_redis_key = "sms_%s" % user.mobile

        redis_conn = get_redis_connection('verify_codes')
        sms_code_redis = redis_conn.get(sms_code_redis_key)

        if not sms_code_redis:
            raise serializers.ValidationError("短信验证码已过期")

        # 比较手机验证码是否正确
        if sms_code_redis.decode() != sms_code:
            raise serializers.ValidationError("短信验证码输入有误")

        return attrs

    # 生成access_token
    def create(self, validated_data):
        # 组织响应数据返回{"user_id": "", "access_token": ""}
        username = validated_data['username']
        access_token = generate_find_password_accesstoken(username)

        # 给validated_data添加属性access_token
        validated_data['access_token'] = access_token
        user = User.objects.get(Q(username=username) | Q(mobile=username))
        validated_data['user_id'] = user.id

        return validated_data

# 第四步-->保存要修改的密码的序列化器
class SavePasswordSerializer(serializers.Serializer):

    password = serializers.CharField(label='新密码', write_only=True, min_length=8, max_length=20,)
    password2 = serializers.CharField(label='新密码重复', write_only=True, min_length=8, max_length=20,)
    access_token = serializers.CharField(label='access_token', write_only=True)
    pk = serializers.IntegerField(label='用户id', write_only=True)

    # 校验密码一致性, 解密access_token并校验是否正确
    def validate(self, attrs):
        password = attrs['password']
        password2 = attrs['password2']
        access_token = attrs['access_token']
        pk = attrs['pk']

        # 检验两次密码是否一致
        if password != password2:
            raise serializers.ValidationError("两次输入密码不一致")

        # access_token 解密校验
        username_dict = decode_accesstoken(access_token)
        # 为空校验
        if not username_dict:
            raise serializers.ValidationError("access_token不正确")

        # 正确校验
        username = username_dict['mobile']
        if not username:
            raise serializers.ValidationError("access_token不正确")
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise serializers.ValidationError("access_token不正确")

        try:
            user2 = User.objects.get(id=pk)
        except User.DoesNotExist:
            raise serializers.ValidationError("用户id有误")

        if user2.id != user.id:
            raise serializers.ValidationError('操作用户不一致')

        return attrs

    def update(self, instance, validated_data):

        password = validated_data['password']

        instance.set_password(password)

        instance.save()

        return instance



# 修改密码序列化器
class UpdateUserSerializer(serializers.Serializer):
    """修改密码序列化器类"""
    old_password = serializers.CharField(label='当前密码', max_length=20)
    password = serializers.CharField(label='新密码', max_length=20)
    password2 = serializers.CharField(label='用于判断新密码', max_length=20)

    def validate(self, attrs):
        old_password = attrs['old_password']
        password = attrs['password']
        password2 = attrs['password2']

        user = self.context['request'].user

        if not user.check_password(old_password):
            raise serializers.ValidationError('原密码输入错误')
        if password != password2:
            raise serializers.ValidationError('两次密码不一致')
        return attrs

    # def create(self, validated_data):
    #
    #     user = self.context['request'].user
    #
    #     user.set_password(validated_data['password'])
    #     user.save()
    #     return user
    def update(self, instance, validated_data):
        instance.set_password(validated_data['password'])
        instance.save()
        return instance

# 继承ModelSerializer,只需要补充模型类中没有的字段即可
class UserSerializer(ModelSerializer):
    #　校验所需要的字段,User模型类里面没有,需要补上
    password2 = serializers.CharField(label='重复密码',write_only=True)
    sms_code = serializers.CharField(label='短信验证码',write_only=True)
    allow = serializers.CharField(label='同意协议',write_only=True)
    token = serializers.CharField(label='JWT token',read_only=True)
    class Meta:
        model = User
        # 指定需要模型类中的哪些字段进行序列操作,自己加上去的也要写再这里面
        fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow','token')
        # 给字段(username,password)增加额外的检测参数,
        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许5-20个字符的用户名',
                    'max_length': '仅允许5-20个字符的用户名',
                }
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

    # 同意协议字段
    def validate_allow(self,value):
        # 判断是否同意协议
        if value != 'true':
            # 抛出异常给客户端返回序列化结果
            raise serializers.ValidationError('用户未同意协议')
        #校验通过返回字典的值
        return value

    # 手机号格式的验证&是否已存在
    def validate_mobile(self,value):
        #　验证手机格式,正则匹配验证
        if not re.match(r'1[3-9]\d{9}',value):
            raise serializers.ValidationError('手机号格式不正确')
        # 通过模型类查询数据库,有没有用户的手机号重复
        count = User.objects.filter(mobile=value).count()
        # 查询的数量大于0,说明手机号已注册
        if count > 0:
            raise serializers.ValidationError('手机号已存在')
        return value

    # 　用户的手机号和用户名均可登录的话，可能会混乱
    def validate_username(self, value):
        # 验证用户名字段的值
        if not re.match(r'^[a-z]\w+', value):
            raise serializers.ValidationError('用户名必须已字母开头')
        return value
    # 对多字段进行校验
    def validate(self, attrs):
        # 密码一致性校验
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError('两次输入的密码不一致')

        # 验证短信验证码正确性
        # 链接redis2号库名verify_codes生成redis对象
        redis_conn = get_redis_connection('verify_codes')
        # 取出用户的手机号 和验证码
        mobile = attrs.get('mobile')
        sms_code = attrs.get('sms_code')
        #　从ｒｅｄｉｓ中取出真验证码
        sms_code_real = redis_conn.get('sms_code_%s' % mobile) # 可能为None， 所以不能在这解码
        # 短信验证码可能会失效
        if not sms_code_real:
            raise serializers.ValidationError('短信验证码已过期')
        #　判断验证码是正确
        if sms_code != sms_code_real.decode():
            raise serializers.ValidationError('验证码输入错误')
        # 成功则返回数据
        return attrs

    # 新增用户
    def create(self, validated_data):
        # 删除前端不需要的字段，序列化
        del validated_data['password2']
        del validated_data['sms_code']
        del validated_data['allow']

        # 这个create_uesr方法能将密码加密存储,
        user = User.objects.create_user(**validated_data)

        # 用户创建成功,注册成功后给客户端一个token,直接记录其身份证,然后下次就可以不用输入用户名密码了
        # 手动签发JWT token的值
        from rest_framework_jwt.settings import api_settings
        # 指定生成payload的方法,pyload可以自定义,但是这个里值返回token值
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        # 生成payload
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        # 给user对象属性一个token属性
        user.token = token

        return user

# 用户中心详情展示序列化器类
class UserDetailSerializer(ModelSerializer):
    """用户序列化器"""
    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'email', 'email_active')

# 邮箱设置的序列化器类
class EmailSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ('id','email')
        extra_kwargs = {
            'email':{
                'required':True
            }
        }

    def update(self, instance, validated_data):
        # 设置登录用户邮箱
        email = validated_data['email']
        instance.email = email
        instance.save()

        # TODO:给邮箱发送验证短信
        # http://www.meiduo.site:8000/succese_verify_email.html?user_id=<用户id>
        #　用id作为验证参数肯呢个会发生恶意请求
        # 将用户的信息进行加密放在链接中

        # 生成验证链接
        verify_url = instance.generate_verify_email_url()
        #　ｃｅｌｅｒｙ异步发送邮件
        send_verify_email.delay(email,verify_url)

        return instance

# 用户收获地址序列化器
class AddressSerializer(ModelSerializer):
    """地址序列化器类"""
    province_id = serializers.IntegerField(label='省id')
    city_id = serializers.IntegerField(label='市id')
    district_id = serializers.IntegerField(label='区县id')
    # 将省市区的名字序列化并返回exxit()
    province = serializers.StringRelatedField(label='省', read_only=True)
    city = serializers.StringRelatedField(label='市', read_only=True)
    district = serializers.StringRelatedField(label='区县', read_only=True)
    class Meta:
        model = Address
        exclude = ('user','is_deleted','create_time','update_time')

    def validate_mobile(self, value):
        # 手机号格式校验--mobile字段是我们自己新增在model中的,email字段django
        if not re.match(r'^1[3-9]\d{9}$',value):
            raise serializers.ValidationError('手机号格式错误')
        return value

    def create(self, validated_data):
        # 创建收获地址并保存到数据库中

        # 获取用户对想
        user = self.context['request'].user
        validated_data['user']=user

        # 调用父类的create方法
        address = super().create(validated_data)
        return address

# 收获地址的标题序列化器
class AddressTitleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = ('id', 'title', )

# 历史浏览记录序列化器
class BrowseHistorySerializer(serializers.Serializer):
    sku_id = serializers.IntegerField(label='SKU商品编号')

    def validate_sku_id(self,value):
        """sku_id的存在性的校验"""
        try:
            sku = SKU.objects.get(id=value)
        except SKU.DoesNotExist:
            raise serializers.ValidationError('商品不存在')
        return value

    def create(self, validated_data):
        """在redis中存储用户浏览记录"""
        redis_conn = get_redis_connection('history')

        user = self.context['request'].user

        # 拼接key
        history_key = 'history_%s' % user.id

        # # 去重：如果商品已经被浏览，需要将商品id先从list列表移除
        sku_id = validated_data['sku_id']
        redis_conn.lrem(history_key,0,sku_id)

        # 保存有序：最新浏览的商品的id添加到list列表最左侧
        redis_conn.lpush(history_key,sku_id)
        # 截取：只保留最新几个浏览商品id
        redis_conn.ltrim(history_key,0,constants.USER_BROWSING_HISTORY_COUNTS_LIMIT-1)

        return validated_data






















